#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Croquette -- by Michael Lee and David Gomes

Please see /README_Croquette.txt
Licensed under the MIT License

Written in Python 2.7
"""

import os
import wx
import wx.stc as stc

from time import strftime as date
from random import choice
import time
import traceback
import threading
import sys
import string
import unicodedata

import ticonverter
import data

#TODO Have the following configurations in a text file
run_program = "wxwabbitemu"

faces = { 'times': 'Times',
          'mono' : 'Courier',
          'helv' : 'Helvetica',
          'other': 'new century schoolbook',
          'size' : 12,
          'size2': 10,
        }

class Editor(stc.StyledTextCtrl):
    """Editor Class, inherits wxStyledTextCtrl"""
    def __init__(self, parent):
        """Calls some functions and sets the Editor up"""
        super(Editor, self).__init__(parent)

        self.filename = ''
        self.directory = '.'
        self.filepath = ''
        self.indent_level = 0

        self.SetGenerics()        
        self.SetMargins()
        self.SetStyles()
        self.SetAutocompletion()

        self.SetText("")  # Looks nicer this way. # No it doesn't :P -alberthrocks


    def SetGenerics(self):
        """Defines default configurations"""
        self.SetLexer(stc.STC_LEX_PYTHON) #is this giving us trouble? 
        self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) #set mono spacing here!
        self.SetTabWidth(4)
        self.SetIndentationGuides(1)
        #Set UTF-8 Coding Style, wxSTC_CP_UTF8
        self.SetCodePage(65001)
        #Indentation will only use space characters if useTabs is false
        self.SetUseTabs(False)

    def SetMargins(self):
        """Sets editor margins"""
        # margin 0 for breakpoints
        self.SetMarginSensitive(0, True)
        self.SetMarginType(0, wx.stc.STC_MARGIN_SYMBOL)
        self.SetMarginMask(0, 0x3)
        self.SetMarginWidth(0, 12)
        # margin 1 for current line arrow
        self.SetMarginSensitive(1, False)
        self.SetMarginMask(1, 0x4)
        # margin 2 for line numbers
        self.SetMarginType(2, stc.STC_MARGIN_NUMBER)
        self.SetMarginWidth(2, 28)

    def SetStyles(self, lang='python'):
        """Sets the styles"""

        #INDICATOR STYLES FOR ERRORS (self.errorMark)
        self.IndicatorSetStyle(2, stc.STC_INDIC_SQUIGGLE)
        self.IndicatorSetForeground(2, wx.RED)
        self.StyleSetSpec(stc.STC_P_DEFAULT, "face:%(mono)s,size:%(size)d" % faces)
        
        # White space
        self.StyleSetSpec(stc.STC_P_DEFAULT, "face:%(mono)s,size:%(size)d" % faces)
        # Number
        self.StyleSetSpec(stc.STC_P_NUMBER, "face:%(mono)s,fore:#007F7F,size:%(size)d" % faces)
        # String
        self.StyleSetSpec(stc.STC_P_STRING, "face:%(mono)s,fore:#7F007F,size:%(size)d" % faces)
        # Single quoted string
        self.StyleSetSpec(stc.STC_P_CHARACTER, "face:%(mono)s,fore:#7F007F,size:%(size)d" % faces)
        # Operators
        self.StyleSetSpec(stc.STC_P_OPERATOR, "face:%(mono)s,bold,size:%(size)d" % faces)
        # Identifiers
        self.StyleSetSpec(stc.STC_P_IDENTIFIER, "")
        # End of line where string is not closed
        self.StyleSetSpec(stc.STC_P_STRINGEOL, "face:%(mono)s,fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces)

    def SetAutocompletion(self):
        """Sets up autocompletion."""
        self.set_keyword_list("axe")        # For now, default to Axe.
        
        self.AutoCompSetAutoHide(True)
        self.AutoCompSetDropRestOfWord(False)
        self.AutoCompSetIgnoreCase(False)
        
        self.Bind(wx.EVT_KEY_UP, self.on_key_up)

    def set_keyword_list(self, mode):
        """Chooses whether to use Axe or Basic keywords for autocompletion.

        Arguments:
         - mode: A string ("axe" or "basic")
        """
        self.tokens = data.tokens_type[mode]

    def on_undo(self, event):
        """Checks if can Undo and if yes undoes"""
        if self.CanUndo() == 1:
            self.Undo()

    def on_redo(self, event):
        """Checks if can Redo and if yes redoes"""
        if self.CanRedo() == 1:
            self.Redo()

    def on_cut(self, event):
        """Cuts selected text"""
        self.Cut()

    def on_copy(self, event):
        """Copies selected text"""
        self.Copy()

    def on_paste(self, event):
        """Pastes selected text"""
        self.Paste()

    def on_clear(self, event):
        """Deletes selected text"""
        self.Clear()

    def on_select_all(self, event):
        """Selects all the text"""
        self.SelectAll()

    def on_key_up(self, event):
        """This deals primarily with autocompletition.

        I have a nasty feeling that the way I wrote this is ugly, so
        a rewrite might be necessary.
        """

        # This won't return the last character if the line doesn't end in '\n'.
        (text, pos) = self.GetCurLineUTF8()

        if not(self.AutoCompActive()):
            # Find the token fragment to compare to the keyword list.
            # 'self.tokens.markers' contains a list of all the characters
            # that could never be part of a token (such as '{')
            cut = -1
            for mark in self.tokens.markers:
                new_cut = text[:pos].rfind(mark)
                if new_cut > cut:
                    cut = new_cut
            # Hack.
            cut += 1

            context = text[cut:pos]
            print text
            keywords = []

            # Run through the keyword list, and add only the ones that
            # starts with the token fragment.
            if len(context):
                for item in self.tokens.words_list:
                    if item.lower().startswith(context.lower()):
                        if item not in keywords:
                            keywords.append(item)
                keywords.sort()

                if keywords:
                    if context not in keywords:   # Otherwise, it gets annoying.
                        self.AutoCompShow(pos - cut, " ".join(keywords))
        event.Skip()
    
    def GetAsciiText(self):
		"""Returns the contents of the textbox in ascii form."""
		#return unicodedata.normalize('NFKD', self.GetText()).encode('ascii','ignore')
		#return self.GetText().encode("ascii")
		return self.GetText()
EVT_RESULT_ID = wx.NewId()
class ResultEvent(wx.PyEvent):
    """Simple event to carry arbitrary result data."""
    def __init__(self, data):
        """Init Result Event."""
        wx.PyEvent.__init__(self)
        self.SetEventType(EVT_RESULT_ID)
        self.data = data
class CroquetteAntifreeze(threading.Thread):
    def __init__(self, wxObject):
        threading.Thread.__init__(self)
        #self.start()    # start the thread
        self.hasUpdated = False
        self.pingToken = []
        self.wxObject = wxObject
        self.stopnow = False
        self.checkExitLock = False
        print "** [CroquetteAntiFreeze] Watchdog thread started!"
    def stopThread(self):
        print "** [CroquetteAntiFreeze] Got signal to exit."
        self.stopnow = True
    def checkExit(self):
        if self.checkExitLock == True:
            print "** [CroquetteAntiFreeze] WARNING - checkExit called, but checkExit is already running!"
            return
        #print "** [CroquetteAntiFreeze] Checking for thread exit..."
        # Lock function from execution
        self.checkExitLock = True
        if self.stopnow == True:
            print "** [CroquetteAntiFreeze] Exiting."
            exit()
        self.checkExitLock = False
    def run(self):
        """Run Worker Thread."""
        import wx
        while self.stopnow != True:
            self.checkExit()
            if len(self.pingToken) > 4:
                print "** [CroquetteAntiFreeze] GUI thread has not responded for 5 seconds! Creating traceback and notifying user..."
                try:
                    import faulthandler
                    print "** [CroquetteAntiFreeze] Using Faulthandler for a better stack trace."
                    #fh = open("croquette_stacktrace.txt", "w")
                    faulthandler.dump_traceback(sys.stderr, True)
                    print "** [CroquetteAntiFreeze] Regular traceback:"
                    print str(traceback.print_stack())
                    print str(traceback.print_exc())
                    #fh.close()
                except:
                    print "** [CroquetteAntiFreeze] Can't load Faulthandler module, so falling back to traceback."
                    print "** [CroquetteAntiFreeze] It's very likely that the output will not be very useful."
                print str(traceback.print_stack())
                print str(traceback.print_exc())
                self.checkExit()
                # Use Tkinter... *shivers*
                import Tkinter, tkMessageBox
                Tkinter.Tk().withdraw()
                resp = tkMessageBox.showwarning('Croquette Antifreeze', 'It seems that Croquette has froze! Continue waiting for Croquette to respond? Clicking No will exit immediately - note that any changes made that are not saved will be lost.', type=tkMessageBox.YESNO)
                #print "** resp is "+str(resp)
                #dlg = wx.MessageDialog(None, 'It seems that Croquette has froze! Continue waiting for Croquette to respond? Clicking No will exit immediately - note that any changes made that are not saved will be lost.', 'Croquette Antifreeze', wx.YES_NO | wx.ICON_WARNING)
                #resp = dlg.ShowModal()
                #dlg.Destroy()
                #if resp == wx.ID_NO:
                if resp == 'no':
                    print "** [CroquetteAntiFreeze] User opted to close, so terminating."
                    #wx.MessageBox('Croquette has been closed. Any changes made that are not saved are lost.\nPlease report this problem to the developers immediately. Along with your report, include the log file croquette_log.txt so that the bug can be fixed.', 'Croquette Antifreeze', wx.OK | wx.ICON_WARNING)
                    wx.Exit()
                    tkMessageBox.showwarning('Croquette Antifreeze', 'Croquette will now be closed. Any changes made that are not saved will be lost.\nPlease report this problem to the developers immediately. Along with your report, include the log file croquette_log.txt so that the bug can be fixed.')
                    #frame.Destroy()
                    os._exit(1)
                else:
                    print "** [CroquetteAntiFreeze] User opted to continue! Clearing PING tokens..."
                    self.pingToken=[]
            self.checkExit()
            random_string = ''.join([choice(string.letters + string.digits) for i in range(8)])
            self.pingToken.append(random_string)
            wx.PostEvent(self.wxObject, ResultEvent(random_string))
            #print "** [CroquetteAntiFreeze] Sent token to GUI: "+random_string
            time.sleep(1)
            self.checkExit()
        print "** [CroquetteAntiFreeze] Main loop exited."
        exit()
    
    def pongAFGUIAlive(self, token):
        """ Function call from main thread indicating that the GUI is alive! """
        #print "** [CroquetteAntiFreeze] Got token from GUI: "+token
        if token in self.pingToken:
            #print "** [CroquetteAntiFreeze] GUI returned a correct token! Clearing PING token..."
            self.pingToken.remove(token)
        else:
            print "** [CroquetteAntiFreeze] GUI returned an invalid token!"

class MainFrame(wx.Frame):
    """Class with the main frame, the GUI and the GUI Functions"""
    def __init__(self, parent,id):
        """Displays the frame, creates the GUI"""
        wx.Frame.__init__(self, parent,id,'Croquette', size=(800,600))
        self.create_gui()
        self.dirname = ""
        self.filepath = ""
        
        self.parser = ticonverter.Parser()
        self.detokenize_type = self.parser.get_detokenize_type()
        
        # Set this blank window as a "save point" - this lets you 
        # close the window sans any dialog if you didn't modify it.
        self.text_input.SetSavePoint()
        self.SetTitle("Untitled - Croquette")
        
        self.file_name = None
        # Antifreeze!
        self.AFRunning = True
        self.Connect(-1, -1, EVT_RESULT_ID, self.OnAFPing)
        self.CroquetteAFHandle = CroquetteAntifreeze(self)
        self.CroquetteAFHandle.start()
        
        self.text_input.SetFocus()
        # NOTE:
        # The parser can auto-detect the filetype and detokenize accordingly, or
        # it can force a conversion to either Axe or Basic.
        #
        # self.parser.set_detokenize_type(None)     # Auto-detect mode
        # self.parser.set_detokenize_type("Axe")    # Force conversion to Axe
        # self.parser.set_detokenize_type("Basic")  # Force conversion to Basic
    
    def OnAFPing(self, event):
        self.CroquetteAFHandle.pongAFGUIAlive(event.data)

    def on_open(self, event):
        """Opens file for reading."""
        dlg = wx.FileDialog(self, "Open a file", self.dirname, 
                            "", "All files (*.*)|*.*|8xp files (.8xp)|*.8xp|txt files (.txt)|*.txt",
                            wx.OPEN)
        if dlg.ShowModal() == wx.ID_OK:
            self.file_name=dlg.GetFilename()
            self.dir_name=dlg.GetDirectory()

            file_path = os.path.join(self.dir_name, self.file_name)
            self.filepath = file_path #Update path
            
            self.SetTitle("%s - Croquette" % self.file_name)

            if self.parser.detect(file_path) == "8xp":
                # Note: based on the detokenizing type you pick
                # (edit menu, Auto-detect, Axe, or Basic),
                # this should automatically return the right type of string.
                self.text_input.SetText(
                    self.parser.convert_8xp_to_string(file_path))
            else:
                file_handle = open(file_path, "r")
                self.text_input.SetText(file_handle.read())
                file_handle.close()
            self.text_input.SetSavePoint()
                
    def on_save(self, event):
        """Saves a file into 8XP"""
        if self.file_name == None:
            res = self.on_saveas(None)
            return res
        else:
            # Dirty hack.
            if self.file_name[-4:].lower() not in [".8xp", ".txt"]:
                self.file_name += ".8xp"
            
            self.path = os.path.join(self.dir_name, self.file_name)
            
            # Note: string_to_8xp doesn't care if the input string is completely Axe,
            #       completely Basic, or a mixture between the two.
            
            # Error parsing!
            try:
                if self.file_name[-4:].lower() == ".8xp":
                    self.parser.convert_string_to_8xp(self.text_input.GetAsciiText(),
                                                    self.path,
                                                    self.file_name[:-4])
                elif self.file_name[-4:].lower() == ".txt":
                    temp = open(self.path, "w")
                    temp.write(text)
                    temp.close()
                self.SetTitle("%s - Croquette" % self.file_name)
                self.text_input.SetSavePoint()
            except IOError:
                dial = wx.MessageDialog(None, 'The file, '+self.file_name+", could not be saved!\nDetailed error:\n"+str(sys.exc_info()[1])+"\nThis error has been logged in 'croquette_log.txt'. Please report it immediately!",
                                    'Error - Croquette IDE',
                                    wx.OK | wx.ICON_ERROR)
                dial.ShowModal()
                dial.Destroy()
                raise
            #except:
                # Put bad evil error handling code here
            
            return True
    
    def on_saveas(self, event):
        """Saves a file into 8XP"""
        dlg = wx.FileDialog(self, "Save a file", self.dirname, 
                            "", "8xp files (.8xp)|*.8xp|txt files (.txt)|*.txt",
                            wx.SAVE | wx.OVERWRITE_PROMPT)
        if dlg.ShowModal() == wx.ID_OK:
            self.file_name = dlg.GetFilename()
            self.file_name = self.file_name.encode("ascii")
            
            self.dir_name = dlg.GetDirectory()
            
            # Dirty hack.
            if self.file_name[-4:].lower() not in [".8xp", ".txt"]:
                self.file_name += ".8xp"
            
            self.path = os.path.join(self.dir_name, self.file_name)
            
            # Note: string_to_8xp doesn't care if the input string is completely Axe,
            #       completely Basic, or a mixture between the two.
            try:
                if self.file_name[-4:].lower() == ".8xp":
                    self.parser.convert_string_to_8xp(self.text_input.GetAsciiText(),
                                                    self.path,
                                                    self.file_name[:-4])
                elif self.file_name[-4:].lower() == ".txt":
                    temp = open(self.path, "w")
                    temp.write(text)
                    temp.close()
            
                self.SetTitle("%s - Croquette" % self.file_name)
                self.text_input.SetSavePoint()
            except:
                dial = wx.MessageDialog(None, 'The file, '+self.file_name+", could not be saved!\nDetailed error:\n"+str(sys.exc_info()[1])+"\nThis error has been logged in 'croquette_log.txt'. Please report it immediately!",
                                    'Error - Croquette IDE',
                                    wx.OK | wx.ICON_ERROR)
                dial.ShowModal()
                dial.Destroy()
                raise
        else:
            return False
        dlg.Destroy()
        return True
    
    def on_stc_savepointreached(self, event):
        print "Save point reached!"
        try:
            self.file_name
        except AttributeError:
            self.file_name = None
        if self.file_name == None:
            self.SetTitle("Untitled - Croquette")
        else:
            self.SetTitle("%s - Croquette" % self.file_name)
    def on_stc_savepointleft(self, event):
        print "Save point left!"
        #time.sleep(100)
        try:
            self.file_name
        except AttributeError:
            self.file_name = None
        if self.file_name == None:
            self.SetTitle("*Untitled - Croquette")
        else:
            self.SetTitle("*%s - Croquette" % self.file_name)
    
    def SafeExit(self):
        # Tell the watchdog to exit!
        print "** Sending signal to watchdog thread to exit..."
        self.CroquetteAFHandle.stopThread()
        
        print "** Watchdog thread has exited! We can now exit!"
        self.Destroy()
    def on_close(self, event):
        """Handles closing frame event"""
        if self.text_input.GetModify():
            try:
                self.file_name
            except AttributeError:
                self.file_name = None
            
            if self.file_name != None:
                dial = wx.MessageDialog(None, 'Do you want to save the changes you made to '+self.file_name+"?",
                                    'Exit Croquette IDE',
                                    wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION)
                resp = dial.ShowModal()
            else:
                dial = wx.MessageDialog(None, 'Do you want to save the changes you made to Untitled?',
                                    'Exit Croquette IDE',
                                    wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION)
                resp = dial.ShowModal()
            dial.Destroy()
            if resp == wx.ID_YES:
                saveres = self.on_save(None)
                if (saveres):
                    self.SafeExit()
            elif resp == wx.ID_NO:
                self.SafeExit()
            
            # Otherwise do nothing!
        else:
            self.SafeExit()
    def on_run(self, event):
        """Runs a 8XP file"""
        os.system("%s %s" % (run_program, self.filepath))


    def refresh_string(self, indents=False):
        """Takes any string in the text editor and cleans it up based on any new options set.

        Arguments:
         - indents: If True, fixes indentation."""
        print "REFRESH"
        self.text_input.SetText(self.parser.refresh_string(self.text_input.GetAsciiText(), indents))

    def on_switch_auto(self, event):
        """When user changes Auto-detect check"""
        if self.auto_switch.IsChecked():
            self.editMenu.Check(self.axe_switch_id, False)
            self.editMenu.Check(self.basic_switch_id, False)
            
            self.parser.set_detokenize_type(None)
            self.refresh_string()

            text = self.text_input.GetAsciiText()
            if not text:
                self.text_input.set_keyword_list("axe")
            elif self.text_input.GetAsciiText()[0] == ".":
                self.text_input.set_keyword_list("axe")
            else:
                self.text_input.set_keyword_list("basic")
        else:
            self.editMenu.Check(self.auto_switch_id, True)

    def on_switch_axe(self, event):
        """When user changes Axe check"""
        if self.axe_switch.IsChecked():
            self.editMenu.Check(self.auto_switch_id, False)
            self.editMenu.Check(self.basic_switch_id, False)
            
            self.parser.set_detokenize_type("axe")
            self.refresh_string()

            self.text_input.set_keyword_list("axe")
        else:
            self.editMenu.Check(self.axe_switch_id, True)

    def on_switch_basic(self, event):
        """When user changes Basic check"""
        if self.basic_switch.IsChecked():
            self.editMenu.Check(self.auto_switch_id, False)
            self.editMenu.Check(self.axe_switch_id, False)
            
            self.parser.set_detokenize_type("basic")
            self.refresh_string()

            self.text_input.set_keyword_list("basic")
        else:
            self.editMenu.Check(self.basic_switch_id, True)

    def on_clean_indents(self, event):
        """Redos the indentation."""
        self.refresh_string(True)
        
    def create_gui(self):
        """Creates and displays the GUI and binds the events"""
        
        self.text_input = Editor(self)
        
        
        #Menu Bar Code
        menuBar = wx.MenuBar()
        fileMenu = wx.Menu()
        open_id = wx.NewId()
        fileMenu.Append(open_id, "Open\tCtrl+O")
        save_id = wx.NewId()
        fileMenu.Append(save_id, "Save\tCtrl+S")
        saveas_id = wx.NewId()
        fileMenu.Append(saveas_id, "Save As...\tCtrl+Shift+S")
        fileMenu.AppendSeparator()
        close_id = wx.NewId()
        fileMenu.Append(close_id, "Close\tCtrl+Q")
        
        self.editMenu = wx.Menu()
        undo_id = wx.NewId()
        self.editMenu.Append(undo_id, "Undo\tCtrl+Z")
        redo_id = wx.NewId()
        self.editMenu.Append(redo_id, "Redo\tCtrl+Y")
        self.editMenu.AppendSeparator()
        cut_id = wx.NewId()
        self.editMenu.Append(cut_id, "Cut\tCtrl+X")
        copy_id = wx.NewId()
        self.editMenu.Append(copy_id, "Copy\tCtrl+C")
        paste_id = wx.NewId()
        self.editMenu.Append(paste_id, "Paste\tCtrl+V")
        clear_id = wx.NewId()
        self.editMenu.Append(clear_id, "Delete")
        self.editMenu.AppendSeparator()
        select_all_id = wx.NewId()
        self.editMenu.Append(select_all_id, "Select All\tCtrl+A")
        self.editMenu.AppendSeparator()

        #Switches for Axe and Basic
        self.auto_switch_id = wx.NewId()
        self.auto_switch = self.editMenu.Append(self.auto_switch_id, "Auto-detect",
                                                kind=wx.ITEM_CHECK)
        self.axe_switch_id = wx.NewId()
        self.axe_switch = self.editMenu.Append(self.axe_switch_id, "Axe",
                                          kind=wx.ITEM_CHECK)
        self.basic_switch_id = wx.NewId()
        self.basic_switch = self.editMenu.Append(self.basic_switch_id,"TI-Basic",
                                       kind=wx.ITEM_CHECK)
        self.editMenu.Check(self.auto_switch_id, True)
        self.editMenu.Check(self.axe_switch_id, False)
        self.editMenu.Check(self.basic_switch_id, False)

        self.editMenu.AppendSeparator()

        self.clean_indents_id = wx.NewId()
        self.clean_indents = self.editMenu.Append(self.clean_indents_id, "Clean indentation")
        
        run_id = wx.NewId()
        runMenu = wx.Menu()
        runMenu.Append(run_id, "Run\tF5")
        menuBar.Append(fileMenu, "&File")
        menuBar.Append(self.editMenu, "&Edit")
        menuBar.Append(runMenu, "&Run")
        self.SetMenuBar(menuBar)
        #End of Menu Bar Code

        self.Bind(wx.EVT_MENU, self.on_open, id=open_id)
        self.Bind(wx.EVT_MENU, self.on_save, id=save_id)
        self.Bind(wx.EVT_MENU, self.on_saveas, id=saveas_id)
        self.Bind(wx.EVT_MENU, self.on_close, id=close_id)
        self.Bind(wx.EVT_CLOSE, self.on_close) #Called when user closes frame

        self.Bind(wx.EVT_MENU, self.text_input.on_undo, id=undo_id)
        self.Bind(wx.EVT_MENU, self.text_input.on_redo, id=redo_id)
        self.Bind(wx.EVT_MENU, self.text_input.on_cut, id=cut_id)
        self.Bind(wx.EVT_MENU, self.text_input.on_copy, id=copy_id)
        self.Bind(wx.EVT_MENU, self.text_input.on_paste, id=paste_id)
        self.Bind(wx.EVT_MENU, self.text_input.on_clear, id=clear_id)
        self.Bind(wx.EVT_MENU, self.text_input.on_select_all, id=select_all_id)
        self.Bind(wx.EVT_MENU, self.on_switch_auto, id=self.auto_switch_id)
        self.Bind(wx.EVT_MENU, self.on_switch_axe, id=self.axe_switch_id)
        self.Bind(wx.EVT_MENU, self.on_switch_basic, id=self.basic_switch_id)
        self.Bind(wx.EVT_MENU, self.on_clean_indents, id=self.clean_indents_id)
        
        self.Bind(wx.stc.EVT_STC_SAVEPOINTLEFT, self.on_stc_savepointleft)
        self.Bind(wx.stc.EVT_STC_SAVEPOINTREACHED, self.on_stc_savepointreached)

        self.Bind(wx.EVT_MENU, self.on_run, id=run_id)

class LogWriter:
    def __init__(self, filename):
        self.logfile = file(filename, 'a')
        self.logfile.write("** Log Session Start - "+date("%Y-%m-%d %H:%M:%S")+" **\n")
        self.enabledate = False
    def write(self, text):
        if self.enabledate == True:
            if text.strip() != "":
                self.logfile.write("["+date("%Y-%m-%d %H:%M:%S")+"] "+text.rstrip()+"\n")
        else:
            self.logfile.write(text)
        self.logfile.flush()

    def close(self):
        self.logfile.write("** Log Session End - "+date("%Y-%m-%d %H:%M:%S")+" **\n")
        self.logfile.close()
    
    def fileno(self):
        return self.logfile.fileno()

def start():
    """Displays the GUI and starts the GUI Loop"""
    
    # Set up the debug logging
    DEBUGGING = 0
    if DEBUGGING == 1:
        global logout
        logout = LogWriter("croquette_log.txt")
        oldstdout = sys.stdout
        sys.stdout = logout
        oldstderr = sys.stderr
        sys.stderr = logout
    try:
        app = wx.PySimpleApp()
        frame = MainFrame(parent=None, id=-1)
        frame.Show()
        #frame.Maximize()
        app.MainLoop()
    except Exception, e:
        print str(traceback.format_exc())
        wx.MessageBox('Yikes - a crash occurred! Please report this to the developers immediately. Along with your report, include the log file croquette_log.txt so that the bug can be fixed.', 'Croquette', wx.OK | wx.ICON_ERROR)
    if DEBUGGING == 1:
        logout.close()
    return


if __name__ == "__main__":
    start()
